package net.pimpas.operation.operation;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

public class OperationFactory {
	
	private static OperationFactory instance;
	
	private static Logger logger = Logger.getLogger(OperationFactory.class);

	private static final Map<OperationEntry, Class<?>> operations = new HashMap<OperationEntry, Class<?>>();
	
	private OperationFactory() {
		
	}
	
	public static Operation<?> find(String name) {
		if(name != null) name = name.toLowerCase();
		Set<OperationEntry> keys = operations.keySet();
		Iterator<OperationEntry> it = keys.iterator();
		OperationEntry entry = null;
		while(it.hasNext()) {
			entry = it.next();
			if(entry.getName().equals(name))
				try {
					return (Operation<?>) operations.get(entry).newInstance();
				} catch (Throwable e) {
					logger.error("Error while trying to instance operation class ["+operations.get(entry)+"]");
					return null;
				}
		}
		return null;
	}
	
	public static Operation<?> find(Class<?> clazz, String name) {
		if(name != null) name = name.toLowerCase();
		Set<OperationEntry> keys = operations.keySet();
		Iterator<OperationEntry> it = keys.iterator();
		OperationEntry entry = null;
		while(it.hasNext()) {
			entry = it.next();
			if(entry.getName().equals(name) &&
					entry.getClazz().equals(clazz)) 
				try {
					return (Operation<?>) operations.get(entry).newInstance();
				} catch (Throwable e) {
					logger.error("Error while trying to instance operation class ["+operations.get(entry)+"]");
					return null;
				}
		}
		return null;
	}
	
	public static OperationFactory getInstance( ) {
		if(instance == null)
			synchronized(operations) {
				if(instance == null)
					instance = new OperationFactory();
			}
		
		return instance;
	}
	
	public void addOperation(String name, Class<?> clazz) {
		synchronized(operations) {
			operations.put(new OperationEntry(name.toLowerCase(), null), clazz);
		}
	}
	
	
	public void addOperation(String name, Class<?> type, Class<?> clazz) {
		synchronized(operations) {
			operations.put(new OperationEntry(name.toLowerCase(), type), clazz);
		}
	}
	
	private static class OperationEntry {
		private String name;
		private Class<?> clazz;
		
		public OperationEntry(String n, Class<?> c) {
			this.setName(n);
			this.setClazz(c);
		}
		
		public Class<?> getClazz() {
			return clazz;
		}
		public void setClazz(Class<?> clazz) {
			this.clazz = clazz;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
	}
}